
using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
using UnityEngine;
public class SubRendererData
{
    public List<Vector3> vertices {get;private set;}
    public List<Vector3> normals {get;private set;}
    public List<int> triangles {get;private set;}
    public List<Vector2> uv  {get;private set;}
    public List<Vector2> uv2  {get;private set;}

    //UV最大值和最小值
    public Vector2 maxUVValue {get;private set;}
    public Vector2 minUVValue {get;private set;}

    private Texture2D mainTexture;
    public Texture2D refMainTexture{
        get{return mainTexture;}
        set{
            mainTexture =value;
            if(mainTexture==null)
            {
                mainTexture = new Texture2D(16,16);
                Color[] colors = new Color[16*16];
                for(var idx=0;idx<colors.Length;idx++)
                    colors[idx] = Color.white;
                mainTexture.SetPixels(colors);
                mainTexture.Apply();
            }
        }
    }

    public Color mainColor = Color.white;
    public float width;
    public float height;
    public float area = 0;
    public Vector2 position;
    //标志它可以独享一行
    public bool singleLineFlags = false;

    public string name;
    public SubRendererData()
    {
        vertices = new List<Vector3>();
        normals = new List<Vector3>();
        triangles =new List<int>();
        uv = new List<Vector2>();

        uv2 = new List<Vector2>();

        this.maxUVValue = new Vector2(float.MinValue,float.MinValue);
        this.minUVValue = new Vector2(float.MaxValue,float.MaxValue);
    }

    public void AddUV(Vector2 vec)
    {
        this.uv.Add(vec);

        this.maxUVValue = Vector2.Max(vec,this.maxUVValue);
        this.minUVValue = Vector2.Min(vec,this.minUVValue);
    }


    public void CalSize()
    {
        float w =  this.maxUVValue.x - this.minUVValue.x;
        float h = this.maxUVValue.y -this.minUVValue.y;

        if(refMainTexture!=null)
        {
            this.width = refMainTexture.width * w;
            this.height = refMainTexture.height * h;
        }
        else
        {
            this.width = 64;
            this.height = 64;
        }
        
        this.area = this.width * this.height;
    }

    public void RecalculateUVBound()
    {
        this.maxUVValue = new Vector2(float.MinValue,float.MinValue);
        this.minUVValue = new Vector2(float.MaxValue,float.MaxValue);
        foreach(var p in uv)
        {
            this.maxUVValue = Vector2.Max(p,this.maxUVValue);
            this.minUVValue = Vector2.Min(p,this.minUVValue);
        }
    }
    public Vector2 Intersect(SubRendererData uv,out bool isIntersect)
    {
        //先计算两个box中心点
        var myCenter = this.position + new Vector2(width,height)*0.5f;
        var otherCenter = uv.position + new Vector2(uv.width,uv.height)*0.5f;

        float dx = (width + uv.width)*0.5f- Mathf.Abs(otherCenter.x - myCenter.x);
        float dy = (height + uv.height)*0.5f -Mathf.Abs(otherCenter.y - myCenter.y);

        isIntersect = dx>0 && dy>0;

        return new Vector2(dx,dy);
    }

    public bool Merge(SubRendererData subRenderer)
    {

        if(subRenderer.mainColor!=mainColor)
        {
            Debug.LogWarningFormat("'{0}'与'{1}颜色不一致 合并后以'{0}'颜色为准！'",name,subRenderer.name);
        }

        int indexOffset = this.vertices.Count;
        this.vertices.AddRange(subRenderer.vertices);
        this.normals.AddRange(subRenderer.normals);
        
        foreach(var tri in subRenderer.triangles)
        {
            this.triangles.Add(tri+indexOffset);
        }
        
        foreach(var vec in subRenderer.uv)
        {
            this.AddUV(vec);
        }

        if(uv.Count!=vertices.Count)
        {
            Debug.LogError("UV和顶点的数据长度不一致");
        }
        
        return true;
    }

    public class UVShell 
    {
        public List<int> triangles = new List<int>();

        public List<int> uv = new List<int>();

        public void Add(int tri)
        {
            triangles.Add(tri);
        }


        public static List<UVShell> SeparateUV(List<int> triangles)
        {
            List<UVShell> uvShells = new List<UVShell>();
            List<HashSet<int>> hashTris = new List<HashSet<int>>();
            for(var idx = 0;idx<triangles.Count;idx+=3)
            {
                int ia = triangles[idx];
                int ib = triangles[idx+1];
                int ic = triangles[idx+2];

                int hashIdx=0;

                bool contains =false;
                foreach(var hashTri in hashTris)
                {
                    if(hashTri.Contains(ia) || hashTri.Contains(ib) || hashTri.Contains(ic))
                    {
                        hashTri.Add(ia);
                        hashTri.Add(ib);
                        hashTri.Add(ic);

                        uvShells[hashIdx].Add(ia);
                        uvShells[hashIdx].Add(ib);
                        uvShells[hashIdx].Add(ic);

                        contains = true;
                        break;
                    }
                    hashIdx++;
                }

                if(!contains || uvShells.Count==0)
                {
                    var uvShell = new UVShell();
                    var hashTri = new HashSet<int>();

                    uvShell.Add(ia);
                    uvShell.Add(ib);
                    uvShell.Add(ic);

                    hashTri.Add(ia);
                    hashTri.Add(ib);
                    hashTri.Add(ic);

                    hashTris.Add(hashTri);
                    uvShells.Add(uvShell);
                }
            }

            for(var idx=0;idx<uvShells.Count;idx++)
            {
                uvShells[idx].uv.AddRange(hashTris[idx].ToList());
            }
            return uvShells;
        }
    }
    public void OptimizeUV()
    {

        if(this.vertices.Count!=this.uv.Count)
            return;

        float w =  this.maxUVValue.x - this.minUVValue.x;
        float h = this.maxUVValue.y -this.minUVValue.y;

        
        //尝试让UV在0~1范围内，解决有些美术喜欢骚操作问题

        //if(w<1.0 || h<1.0)
        {
            List<UVShell>  uvShells = UVShell.SeparateUV(this.triangles);

            foreach(var uvShell in uvShells)
            {
                Vector2 minShell = new Vector2(float.MaxValue,float.MaxValue);
                Vector2 maxShell = new Vector2(float.MinValue,float.MinValue);

                foreach(var tri in uvShell.triangles)
                {
                    minShell = Vector2.Min(this.uv[tri],minShell);
                    maxShell = Vector2.Max(this.uv[tri],maxShell);
                }

                
                float shellW = maxShell.x - minShell.x;
                float shellH = maxShell.y - minShell.y;

                if( maxShell.x<=1 &&minShell.x>=0 && maxShell.y<=1 &&minShell.y>=0)
                {
                    continue;
                }

                Vector2 shellCenter = (maxShell+minShell)*0.5f;

                Vector2Int offsetIndex = GetUVOffsetIndex(shellCenter);

                //if(minShell.x<0 && maxShell.x<0 ||minShell.y>1 || minShell.y<0 && maxShell.y<0 ||minShell.y>1)
                {
                    //不应该是这样子
                    foreach(var idx in uvShell.uv)
                    {
                        this.uv[idx] = this.uv[idx] -offsetIndex;
                    }
                }
            }

        }

            RecalculateUVBound();
            this.CalSize();
    }

    private Vector2Int GetUVOffsetIndex(Vector2 uv)
    {
        int x = uv.x>=0?(int)uv.x:(int)uv.x-1;
        int y = uv.y>=0?(int)uv.y:(int)uv.y-1;
        return new Vector2Int(x,y);
    }
    public float Fract(float value)
    {
            return value - Mathf.Floor(value);
    }
}